package com.cashmama.market.api.domain.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cashmama.market.api.domain.convert.EnumConvertList;
import com.cashmama.market.api.domain.enums.OrderRecordEnum;
import com.cashmama.market.api.domain.handler.ConfigHandler;
import com.cashmama.market.api.domain.handler.OrderStatusHandler;
import com.cashmama.market.api.domain.vo.RequestLocalInfo;
import com.cashmama.market.api.domain.vo.openapi.OpenApiResponse;
import com.cashmama.market.api.domain.vo.openapi.request.RepayRequest;
import com.cashmama.market.api.domain.vo.openapi.request.TrialRequest;
import com.cashmama.market.api.domain.vo.openapi.response.RepayResultVO;
import com.cashmama.market.api.domain.vo.order.*;
import com.cashmama.market.api.domain.vo.order.repayment.PaymentVO;
import com.cashmama.market.api.domain.vo.order.repayment.RepayAmountInfo;
import com.cashmama.market.api.domain.vo.order.repayment.RepayInfo;
import com.cashmama.market.api.domain.vo.order.repayment.RepaymentDetail;
import com.cashmama.market.api.infras.config.ApolloConfigUtil;
import com.cashmama.market.api.infras.consts.OrderConsts;
import com.cashmama.market.api.infras.consts.RedisKeyConsts;
import com.cashmama.market.api.infras.consts.TimeConsts;
import com.cashmama.market.api.infras.enums.BasicErrorCodeEnum;
import com.cashmama.market.api.infras.enums.JumpRouterEnum;
import com.cashmama.market.api.infras.enums.error.OrderRestErrorEnum;
import com.cashmama.market.api.infras.enums.order.*;
import com.cashmama.market.api.infras.exception.ApplicationException;
import com.cashmama.market.api.infras.utils.DateUtil;
import com.cashmama.market.api.infras.utils.DecimalUtils;
import com.cashmama.market.api.infras.utils.SensitiveInfoUtils;
import com.fqgj.base.services.redis.RedisClient;
import com.fqgj.common.api.Page;
import com.fqgj.common.api.Response;
import com.fqgj.common.utils.CollectionUtils;
import com.fqgj.common.utils.EmojiUtil;
import com.fqgj.common.utils.TimeUtils;
import com.fqgj.xjd.user.client.UserBankService;
import com.fqgj.xjd.user.client.UserBehaviorService;
import com.fqgj.xjd.user.client.UserContactService;
import com.fqgj.xjd.user.client.UserInfoService;
import com.fqgj.xjd.user.client.response.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jsoniter.JsonIterator;
import com.jsoniter.output.JsonStream;
import com.qianli.cashmama.pay.client.domain.payment.request.RepaymentPersonInfo;
import com.qianli.cashmama.pay.client.domain.payment.request.RepaymentRequest;
import com.qianli.cashmama.pay.client.domain.payment.response.RepaymentResponse;
import com.qianli.cashmama.pay.client.enums.PaymentChannelEnum;
import com.qianli.cashmama.pay.client.enums.RepaymentFundChannelEnum;
import com.qianli.cashmama.pay.client.enums.RepaymentTypeEnum;
import com.qianli.cashmama.pay.client.service.PayService;
import com.qianli.cashmama.product.facade.ProductFacade;
import com.qianli.cashmama.product.facade.result.ProductCategory;
import com.qianli.cashmama.trade.client.bill.BillApplicationService;
import com.qianli.cashmama.trade.client.bill.BillQueryService;
import com.qianli.cashmama.trade.client.bill.request.BillPayRequest;
import com.qianli.cashmama.trade.client.bill.response.BillRepaymentResponse;
import com.qianli.cashmama.trade.client.offer.QuoteQueryService;
import com.qianli.cashmama.trade.client.offer.response.OfferResponse;
import com.qianli.cashmama.trade.client.trade.TradeApplicationService;
import com.qianli.cashmama.trade.client.trade.TradeQueryService;
import com.qianli.cashmama.trade.client.trade.request.*;
import com.qianli.cashmama.trade.client.trade.response.*;
import com.qianli.cashmama.trade.client.vo.BillVO;
import com.qianli.cashmama.trade.client.vo.DiscountListVO;
import com.qianli.cashmama.trade.client.vo.OfferVO;
import com.qianli.cashmama.trade.client.vo.TradeVO;
import com.qianli.cashmama.trade.common.enums.*;
import com.qianli.cashmama.trade.common.utils.CalculatorUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static com.qianli.cashmama.trade.common.enums.BillPaidStatusEnum.CLEAR_REPAYMENT;

/**
 * @program: market-api
 * @description
 * @author: frank
 * @create: 2019-12-24 17:49
 **/
@Service
public class OrderService {
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderService.class);
    private static final String SECRET = "YKVISMFTMWXSUECAYZGKAYV";
    @Autowired
    private ApolloConfigUtil apolloConfigUtil;
    @Autowired
    private QuoteQueryService quoteQueryService;
    @Autowired
    private ProductFacade productFacade;
    @Autowired
    private UserBankService userBankService;
    @Autowired
    private ApiTradeService tradeIntegrationService;
    @Autowired
    private ApiUserService apiUserService;
    @Autowired
    private ApiUserQuotaService apiUserQuotaService;
    @Autowired
    private TradeApplicationService tradeApplicationService;
    @Autowired
    private TradeQueryService tradeQueryService;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private UserBehaviorService userBehaviorService;
    @Autowired
    private BillQueryService billQueryService;
    @Autowired
    private BillApplicationService billApplicationService;
    @Autowired
    private OrderStatusHandler orderStatusHandler;
    @Autowired
    private ConfigHandler configHandler;
    @Autowired
    private ApiTradeService apiTradeService;
    @Autowired
    private ApiProductService apiProductService;
    @Autowired
    private UserContactService userContactService;
    @Autowired
    private PayService payService;
    @Autowired
    private UserInfoService userInfoService;

    /**
     * 订单确认页
     *
     * @param productCode
     * @return
     */
    public OrderConfirmDetailVO orderConfirmInfo(String productCode, String merchantCode) {
        String mobile = RequestLocalInfo.getCurrentUser().getMobile();
        String userCode = RequestLocalInfo.getCurrentUser().getUserCode();
        Response<OfferResponse> offerResponse = quoteQueryService.quoteDetail(productCode);
        Response<ProductCategory> productCategoryResponse = productFacade.getCategoryByCode(offerResponse.getData().getProductCategory());
        ProductCategory productCategory = productCategoryResponse.getData();
        OfferResponse offer = offerResponse.getData();
        OrderConfirmDetailVO orderConfirmDetailVO = new OrderConfirmDetailVO();
        TrialRequest request = new TrialRequest();
        request.setMobile(MD5.md5(mobile).toLowerCase());
        request.setProductId(merchantCode);
        request.setApplicationAmount(offer.getBorrowTotalCapital());
        request.setApplicationTerm(String.valueOf(offer.getPeriodLength()));
        request.setTermUnit("1");
        request.setOrgId(productCategory.getMerchantCode());
        String bizData = JSONObject.toJSONString(request);

//        if (apolloConfigUtil.contractUrlSyncSwitch()) {
//            OpenApiResponse result = CallHttpService.callMerchantMethod(productCategory.getMerchantDoaminUrl(), productCategory.getMerchantPublicKey(), "gupta.trial.calculation", productCategory.getMerchantCode(), bizData);
//            if (result != null) {
//                TrialResultVO trialResultVO = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), TrialResultVO.class);
//            }
//        }

        orderConfirmDetailVO.setCategoryLogo(productCategory.getLogo())
                .setCategoryDesc(productCategory.getCategoryDesc())
                .setCategoryName(productCategory.getName())
                .setProductCode(productCode);
        orderConfirmDetailVO.setRepaymentPlans(this.repaymentPlanInfo(offer));
        orderConfirmDetailVO.setUsageOfLoanList(EnumConvertList.convert2List());
        orderConfirmDetailVO.setOrderViewInfo(OrderConfirmDetailVO.convert2OrderViewInfo(offer));
        Response<UserBankInfo> userBankInfoResponse = userBankService.getByUserCode(userCode);
        UserBankInfo userBankInfo = userBankInfoResponse.getData();
        String bankNo = null;
        if (userBankInfo != null) {
            bankNo = userBankInfo.getCardNo();
        }
        boolean isGpVersion = apolloConfigUtil.getGPfeescontrollecategories().contains(offerResponse.getData().getProductCategory());


        orderConfirmDetailVO.setOtherInfo(OrderConfirmDetailVO.convert2OtherInfo(offer, SensitiveInfoUtils.bankCardNo(bankNo), apolloConfigUtil.getLoanAgreementsUrl(), apolloConfigUtil.getBankConfirmTips(), isGpVersion));


        orderConfirmDetailVO.setPermissionsInfo(configHandler.getPermissionsInfoFromString(productCategory.getName()));
        orderConfirmDetailVO.setMerchantCode(merchantCode);
        Response<UserContactAddressBook> userDeviceContactByUserCode = userContactService.getUserDeviceContactByUserCode(userCode);
        Response<UserContactSms> userDeviceSmsByUserCode = userContactService.getUserDeviceSmsByUserCode(userCode);
        if ((userDeviceContactByUserCode.getData() != null
                && DateUtils.isSameDay(new Date(), userDeviceContactByUserCode.getData().getGmtCreate()))
                && (userDeviceSmsByUserCode.getData() != null && DateUtils.isSameDay(new Date(), userDeviceContactByUserCode.getData().getGmtCreate()))) {
            orderConfirmDetailVO.setNeedUploadData(false);
        }
        try {
            Response<Boolean> response = tradeQueryService.needPayUserServiceFee(userCode);
            orderConfirmDetailVO.setNeedPayServiceFee(response.getData());
        } catch (Exception e) {
            LOGGER.error("会员服务费获取错误 e:{}", e);
        }

        return orderConfirmDetailVO;
    }

    /**
     * 订单确认页还款计划
     *
     * @param offer
     * @return
     */
    private List<RepaymentPlanVO> repaymentPlanInfo(OfferResponse offer) {

        List<RepaymentPlanVO> repaymentPlans = Lists.newArrayList();
        for (OfferVO offerVO : offer.getOfferList()) {
            RepaymentPlanVO repaymentPlanVO = new RepaymentPlanVO();
            String repaymentAmount = DecimalUtils.formatDecimal(new BigDecimal(offer.getPeriodRepaymentCapital()));

            boolean isGpVersion = apolloConfigUtil.getGPfeescontrollecategories().contains(offer.getProductCategory());
            if (isGpVersion) {
                repaymentAmount = DecimalUtils.formatDecimal(new BigDecimal(offer.getBorrowTotalCapital()).add(new BigDecimal(offer.getGstCharge())).add(new BigDecimal(offer.getTotalServiceFee())).add(new BigDecimal(offer.getTotalInterestFee())));
            }

            repaymentPlanVO.setInterest(DecimalUtils.formatDecimal(new BigDecimal(offerVO.getInterestFee())))
                    .setPrincipal(DecimalUtils.formatDecimal(new BigDecimal(offerVO.getBorrowCapital())))
                    .setRepaymentAmount("₹" + repaymentAmount)
                    .setRepaymentDate(DateUtil.getDate(DateUtil.addDate(new Date(), offerVO.getCurrentPeriod() * offer.getPeriodLength() - 1)));
            repaymentPlans.add(repaymentPlanVO);
        }
        return repaymentPlans;
    }

    /**
     * 允许申请的时间段
     */
    public void permitApplyTime() {
        SimpleDateFormat df = new SimpleDateFormat("HH:mm");// 设置日期格式
        Date now = null;
        Date beginTime = null;
        Date endTime = null;
        try {
            now = df.parse(df.format(new Date()));
            beginTime = df.parse(apolloConfigUtil.permitApplyTimeRange().split(";")[0]);
            endTime = df.parse(apolloConfigUtil.permitApplyTimeRange().split(";")[1]);
        } catch (Exception e) {
            LOGGER.error("============ transfer date error ===========");
        }
        if (!DateUtil.belongCalendar(now, beginTime, endTime)) {
            throw new ApplicationException(OrderRestErrorEnum.ORDER_APPLY_NOT_PERMIT_IN_THIS_TIME);
        }
    }

    /**
     * 用户认证项判断
     *
     * @param userCode
     */
    public void checkProfileStatus(String userCode) {
        String mobile = RequestLocalInfo.getCurrentUser().getMobile();
        //判断个人资料状态
        boolean profileFinished = apiUserService.allActionComplete(userCode);
        if (!profileFinished) {
            throw new ApplicationException(OrderRestErrorEnum.PROFILE_NOT_FINISHED);
        }
    }

    /**
     * 生成订单
     *
     * @param userCode
     * @param productCode
     * @param channelCode
     * @param appClient
     */
    public Response<CreateTradeResponse> createOrderInfo(String merchantCode, String userCode, String productCode, String productCategory, String channelCode, String appClient, Integer usageOfLoan, String ip) {
        Integer clientType = AppRequestTypeEnum.getCode(appClient);
        TradeClientTypeEnum tradeClientTypeEnum = TradeClientTypeEnum.getByType(clientType);
        CreateTradeRequest tradeRequest = new CreateTradeRequest();
        tradeRequest.setUserCode(userCode);
        tradeRequest.setProductCode(productCode);
        tradeRequest.setChannelCode(channelCode);
        tradeRequest.setClientType(tradeClientTypeEnum);
        tradeRequest.setProductCategory(productCategory);
        tradeRequest.setTradeType(TradeTypeEnum.INNER);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("ip", ip);
        tradeRequest.setExtraData(jsonObject.toJSONString());
        TradeExtraData tradeExtraData = new TradeExtraData()
                .setAppCode(RequestLocalInfo.getRequestBasicInfo().getAppCode())
                .setIp(ip)
                .setVersion(RequestLocalInfo.getRequestBasicInfo().getAppVersion())
                .setVersionCode(RequestLocalInfo.getRequestBasicInfo().getVersionCode() + "")
                .setPackageName(RequestLocalInfo.getRequestBasicInfo().getPackageName())
                .setAppsflyerId(RequestLocalInfo.getRequestBasicInfo().getAppsflyerId());
        tradeRequest.setTradeExtraData(tradeExtraData);
        List<DiscountListVO> discountList = new ArrayList<>();
        tradeRequest.setDiscountList(discountList);
        tradeRequest.setUsageOfLoan(UsageOfLoanEnum.getByCode(usageOfLoan));
        tradeRequest.setMerchantCode(merchantCode);
        Response<CreateTradeResponse> tradeResponseResponse = tradeApplicationService.createTrade(tradeRequest);
        if (!tradeResponseResponse.isSuccess()) {
            throw new ApplicationException(tradeResponseResponse.getMsg());
        }
        LOGGER.info("＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃创建交易成功userCode:{},tradeNo:{},merchantCode:{}＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃", userCode, tradeResponseResponse.getData().getTradeNo(), merchantCode);
        if (apiTradeService.isNewUser(userCode, merchantCode)) {
            //记录新用户下单数量
            redisClient.incrBy(RedisKeyConsts.NEW_USER_ORDERS_NUMBER, 1L, merchantCode, DateUtil.getDate("yyyy-MM-dd"));
            redisClient.expire(RedisKeyConsts.NEW_USER_ORDERS_NUMBER, TimeConsts.ONE_DAY_AND_HALF, merchantCode, DateUtil.getDate("yyyy-MM-dd"));
        } else {
            //记录老用户下单数量
            redisClient.incrBy(RedisKeyConsts.OLD_USER_ORDERS_NUMBER, 1L, merchantCode, DateUtil.getDate("yyyy-MM-dd"));
            redisClient.expire(RedisKeyConsts.OLD_USER_ORDERS_NUMBER, TimeConsts.ONE_DAY_AND_HALF, merchantCode, DateUtil.getDate("yyyy-MM-dd"));
        }
        return tradeResponseResponse;
    }

    /**
     * 新增wifi信息
     *
     * @param userCode
     * @param userWifi
     * @param nearbyWifis
     */
    public void addWifiInfo(String userCode, UserWifiDTO userWifi, List<UserWifiDTO> nearbyWifis) {
        if (Objects.isNull(userWifi) || StringUtils.isBlank(userWifi.getBssid()) || StringUtils.isBlank(userWifi.getSsid())) {
            return;
        }
        List<UserWifi> userWifis = Lists.newArrayList();
        userWifis.add(buildUserWifi(userCode, userWifi, WifiTypeEnum.CURRENT_WIFI));

        if (org.apache.commons.collections.CollectionUtils.isNotEmpty(nearbyWifis)) {
            List<UserWifi> nearByuserWifi = nearbyWifis.stream().filter(userWifiVO2 -> StringUtils.isNotBlank(userWifiVO2.getSsid())).map(userWifiVO1 -> buildUserWifi(userCode, userWifiVO1, WifiTypeEnum.NEARBY_WIFI)).collect(Collectors.toList());
            userWifis.addAll(nearByuserWifi);
        }
        userBehaviorService.batchAddWifi(userWifis);

    }

    private UserWifi buildUserWifi(String userCode, UserWifiDTO userWifiDTO, WifiTypeEnum wifiTypeEnum) {
        UserWifi userWifi = new UserWifi();

        String ssid = userWifiDTO.getSsid();
        ssid = EmojiUtil.filterEmoji(ssid);
        if (StringUtils.isBlank(ssid)) {
            ssid = "MOCK " + wifiTypeEnum.getDesc();
        }

        return userWifi.setBssid(userWifiDTO.getBssid())
                .setSsid(ssid)
                .setType(wifiTypeEnum.getCode())
                .setUserCode(userCode);
    }

    /**
     * 获取订单详情
     *
     * @param tradeNo
     * @return
     */
    public OrderDetail getOrderDetailInfo(String tradeNo) {
        if (apolloConfigUtil.tradeQuery3dSync()) {
            billApplicationService.orderStatus(tradeNo);
            Response paymentPlan = billApplicationService.getPaymentPlan(tradeNo);
            LOGGER.info("==== 获取还款计划 :{} =====", JSONObject.toJSONString(paymentPlan));
        }
        OrderDetail orderDetail = new OrderDetail();
        Response<TradeInfoResponse> tradeInfoResponse = tradeQueryService.queryByTradeNo(tradeNo);
        if (tradeInfoResponse.getData() == null) {
            throw new ApplicationException(OrderRestErrorEnum.ORDER_NOT_EXSIT);
        }
        TradeInfoResponse tradeInfo = tradeInfoResponse.getData();
        List<BillVO> repayOrderBills = tradeIntegrationService.getRepayOrderBills(tradeInfo.getTradeNo(), tradeInfo.getUserCode(),
                tradeInfo.getProductCategory(), BillTypeEnum.ALL_BILL.getCode());
        Response<ProductCategory> productCategoryResponse = productFacade.getCategoryByCode(tradeInfo.getProductCategory());
        ProductCategory productCategory = productCategoryResponse.getData();
        orderDetail.setCategoryLogo(productCategory.getLogo())
                .setCategoryName(productCategory.getName())
                .setCategoryCode(tradeInfo.getProductCategory());
        orderDetail.setOrderApplyDate(DateUtil.getDate(tradeInfo.getApplyDate()));
        orderDetail.setOrderInfo(this.composeOrderInfo(tradeInfo, repayOrderBills, tradeInfo.getProductCategory(), productCategory.getName()));
        orderDetail.setRepaymentPlans(this.composeRepaymentPlan(repayOrderBills));
        Map param = Maps.newHashMap();
        param.put("order_no", tradeNo);
        String contactUrl = "";

        if (apolloConfigUtil.contractUrlSyncSwitch()) {
            OpenApiResponse result = CallHttpService.callMerchantMethod(productCategory.getMerchantDoaminUrl(), productCategory.getMerchantPublicKey(), "/gupta.deal.contract", productCategory.getMerchantCode(), JSONObject.toJSONString(param));
            if (result != null && result.getCode() == BasicErrorCodeEnum.OPEN_API_SUCCESS.getCode()) {
                contactUrl = JSONObject.parseObject(JSONObject.toJSONString(result.getData())).getString("contract_url");
            }
        }
        orderDetail.setOrderFieldDetails(this.composeOrderFieldDetails(tradeInfo, repayOrderBills, contactUrl));
        orderDetail.setMsgInfo(this.buildMsgInfo(productCategory.getName()));
        orderDetail.setOrderWithdrawInfo(this.composeOrderWithdrawInfo(tradeInfo));
        orderDetail.setOrderWithdrawButton(this.composeOrderWithdrawButton(tradeInfo));
        return orderDetail;
    }

    /**
     * 提现展示语
     *
     * @return
     */
    private OrderWithdrawInfo composeOrderWithdrawInfo(TradeInfoResponse tradeInfo) {
        LOGGER.info("==== 是否是提现状态:{} ====", TradeStatusEnum.PENDING_WITHDRAWAL.equals(tradeInfo.getOrderStatus()));
        if (TradeStatusEnum.PENDING_WITHDRAWAL.equals(tradeInfo.getOrderStatus())) {
            String amount = "₹" + new BigDecimal(tradeInfo.getBorrowTotalCapital()).setScale(0, BigDecimal.ROUND_HALF_UP).toPlainString();
            OrderWithdrawInfo orderWithdrawInfo = new OrderWithdrawInfo();
            orderWithdrawInfo.setTitle("Approved");
            orderWithdrawInfo.setPrincipal(amount);
            String tips = TipsEnum.PENDING_WITHDRAWAL2.getDescription().replaceAll("##AMOUNT##", new BigDecimal(tradeInfo.getBorrowTotalCapital()).setScale(0, BigDecimal.ROUND_HALF_UP).toPlainString());
            orderWithdrawInfo.setTips(tips);
            orderWithdrawInfo.setHighlightTextList(Arrays.asList(amount, "24 hours"));
            String next = "₹3000";
            int value = new BigDecimal(tradeInfo.getBorrowTotalCapital()).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
            if (value == 2000) {
                next = "₹3000";
            } else if (value == 3000) {
                next = "₹5000";
            } else if (value == 5000) {
                next = "₹8000";
            } else if (value == 8000) {
                next = "₹8000";
            }
            orderWithdrawInfo.setNextPrincipal(next);
            orderWithdrawInfo.setNextTitle("Next Loan");
            orderWithdrawInfo.setNextTips("Repay on time get high amount");
            return orderWithdrawInfo;
        }
        return null;
    }

    /**
     * 提现按钮文案
     *
     * @return
     */
    private OrderWithdrawButton composeOrderWithdrawButton(TradeInfoResponse tradeInfo) {
        if (TradeStatusEnum.PENDING_WITHDRAWAL.equals(tradeInfo.getOrderStatus())) {
            OrderWithdrawButton button = new OrderWithdrawButton();
            button.setButtonText1("Withdraw Right Now");
            button.setButtonText2("directly into your bank card");
            return button;
        }
        return null;
    }

    /**
     * 还款提示语
     *
     * @return
     */
    private MsgInfoVO buildMsgInfo(String name) {
        MsgInfoVO msgInfo = null;
        try {
            msgInfo = JSONObject.parseObject(apolloConfigUtil.getRepayMsgInfo(), MsgInfoVO.class);
        } catch (Exception e) {
            LOGGER.error("============== build msg info error, e:{} ==============", e);
        }
        msgInfo.setMsg(String.format(msgInfo.getMsg(), name));
        return msgInfo;
    }

    private OrderCombinationInfo composeOrderInfo(TradeInfoResponse tradeInfo, List<BillVO> bills, String categoryCode, String merchantName) {

        BorrowStatusEnum borrowStatusDisplay = BorrowStatusEnum.PENDING;
        BorrowStatusEnum borrowStatusReal = BorrowStatusEnum.PENDING;
        String buttonMsg = ButtonTextEnum.GET_NOW_TEXT.getDesc();
        String buttonJumpUrl = JumpRouterEnum.ORDER_DETAIL.getJumpUrl() + "?tradeNo=" + tradeInfo.getTradeNo();
        String tips = TipsEnum.REVIEWING.getName() + " by " + merchantName;

        TradeStatusEnum tradeStatusEnum = tradeInfo.getOrderStatus();

        //订单关闭
        boolean completed = tradeStatusEnum.equals(TradeStatusEnum.EARLY_REPAYMENT) ||
                tradeStatusEnum.equals(TradeStatusEnum.NORMAL_REPAYMENT) ||
                tradeStatusEnum.equals(TradeStatusEnum.OVERDUE_REPAYMENT) ||
                tradeStatusEnum.equals(TradeStatusEnum.MANUAL_CLOSED);

        if (completed) {
            borrowStatusDisplay = BorrowStatusEnum.AWAIT_BORROW;
            borrowStatusReal = BorrowStatusEnum.AWAIT_BORROW;
            tips = TipsEnum.COMPLETED.getName();
            buttonJumpUrl = JumpRouterEnum.PRODUCT_DETAIL.getJumpUrl() + "?categoryCode=" + categoryCode + "&merchantCode=" + tradeInfo.getMerchantCode();
        }

        if (tradeStatusEnum.equals(TradeStatusEnum.AUDIT_REJECT)) {
            borrowStatusDisplay = BorrowStatusEnum.AWAIT_BORROW;
            borrowStatusReal = BorrowStatusEnum.AWAIT_BORROW;
            tips = TipsEnum.REFUSED.getName();
            buttonJumpUrl = JumpRouterEnum.PRODUCT_DETAIL.getJumpUrl() + "?categoryCode=" + categoryCode + "&merchantCode=" + tradeInfo.getMerchantCode();
        }

        //待提现
        if (TradeStatusEnum.PENDING_WITHDRAWAL.equals(tradeStatusEnum)) {
            borrowStatusDisplay = BorrowStatusEnum.PENDING_WITHDRAWAL;
            borrowStatusReal = BorrowStatusEnum.PENDING_WITHDRAWAL;
            buttonMsg = BorrowStatusEnum.PENDING_WITHDRAWAL.getTitle();
            buttonJumpUrl = JumpRouterEnum.ORDER_DETAIL.getJumpUrl() + "?tradeNo=" + tradeInfo.getTradeNo();
            tips = TipsEnum.PENDING_WITHDRAWAL.getName();
        }

        //放款中
        if (TradeStatusEnum.LOAN_WAITTING.equals(tradeStatusEnum)) {
            borrowStatusDisplay = BorrowStatusEnum.TRANSFERRING;
            borrowStatusReal = BorrowStatusEnum.TRANSFERRING;
            buttonMsg = ButtonTextEnum.TRANSFERRING_TEXT.getDesc();
            tips = TipsEnum.TRANSFERRING.getDescription();
        }

        //放款失败+重绑卡
        if (TradeStatusEnum.PAY_RETRY.equals(tradeStatusEnum)) {
            borrowStatusDisplay = BorrowStatusEnum.PENDING_FAILD;
            borrowStatusReal = BorrowStatusEnum.PENDING_FAILD;
            buttonMsg = ButtonTextEnum.NOW_CHANGE_TEXT.getDesc();
            buttonJumpUrl = JumpRouterEnum.PERSONAL_INFO_BANK.getJumpUrl();
            tips = TipsEnum.TRANSFER_FAILED.getName();
        }

        //待还款
        if (Objects.equals(TradeStatusEnum.REPAYMENT_WATTING, tradeStatusEnum)) {
            BigDecimal repaymentAmount = getRepayAmountForBills(bills);
            borrowStatusDisplay = BorrowStatusEnum.REPAY;
            borrowStatusReal = BorrowStatusEnum.REPAY;
            buttonMsg = "Repay ₹" + DecimalUtils.formatDecimal(repaymentAmount);
            tips = TipsEnum.REPAY.getName();
        }

        if (Objects.equals(TradeStatusEnum.OVERDUE, tradeStatusEnum)) {
            BigDecimal repaymentAmount = getRepayAmountForBills(bills);
            borrowStatusDisplay = BorrowStatusEnum.OVER_DUE;
            borrowStatusReal = BorrowStatusEnum.OVER_DUE;
            buttonMsg = "Repay ₹" + DecimalUtils.formatDecimal(repaymentAmount);
            tips = TipsEnum.REPAY.getName();
        }
        OrderCombinationInfo orderCombinationInfo = new OrderCombinationInfo()
                .setTradeNo(tradeInfo.getTradeNo())
                .setButtonText(buttonMsg)
                .setTips(tips)
                .setStatus(borrowStatusDisplay.getCode())
                .setStatusText(borrowStatusReal.getTitle())
                .setJumpUrl(buttonJumpUrl);
        this.composeBillInfo(orderCombinationInfo, bills);
        return orderCombinationInfo;
    }

    private void composeBillInfo(OrderCombinationInfo orderCombinationInfo, List<BillVO> bills) {

        if (CollectionUtils.isEmpty(bills)) {
            return;
        }
        BillVO billVO = bills.get(0);
        BigDecimal repaymentCapital = CalculatorUtils.repaymentTotalCapital(new BigDecimal(billVO.getBorrowCapital()),
                new BigDecimal(billVO.getInterest()), new BigDecimal(billVO.getOverdueFee())
                , new BigDecimal(billVO.getOverdueServiceFee()));
        orderCombinationInfo.setCurrentPeriod(billVO.getCurrentPeriod())
                .setInterest(DecimalUtils.formatDecimal(new BigDecimal(billVO.getInterest())))
                .setPrincipal(DecimalUtils.formatDecimal(new BigDecimal(billVO.getBorrowCapital())))
                .setRepaymentAmount("₹" + DecimalUtils.formatDecimal(repaymentCapital))
                .setRepaymentDate(DateUtil.getDate(billVO.getRepaymentDate()))
                .setBillNo(billVO.getBillNo())
                .setOverdueFee(DecimalUtils.formatDecimal(DecimalUtils.add(new BigDecimal(billVO.getOverdueFee()), new BigDecimal(billVO.getOverdueServiceFee()))));
        if (!Objects.equals(billVO.getPaidStatus(), BillPaidStatusEnum.CLEAR_REPAYMENT) && CalculatorUtils.isOverdue(billVO.getRepaymentDate(), new Date())) {
            orderCombinationInfo.setStatus(BorrowStatusEnum.OVER_DUE.getCode());
            orderCombinationInfo.setStatusText(BorrowStatusEnum.OVER_DUE.getDesc());
            orderCombinationInfo.setOverdueDays(DateUtil.diffDate(new Date(), billVO.getRepaymentDate()));
        }
    }

    /**
     * 组装还款计划
     *
     * @param bills
     * @return
     */
    private List<RepaymentPlanDetailVO> composeRepaymentPlan(List<BillVO> bills) {
        List<RepaymentPlanDetailVO> repaymentPlanDetails = Lists.newArrayList();
        if (CollectionUtils.isEmpty(bills)) {
            return repaymentPlanDetails;
        }
        for (BillVO bill : bills) {
            BigDecimal repaymentCapital = CalculatorUtils.repaymentTotalCapital(new BigDecimal(bill.getBorrowCapital()),
                    new BigDecimal(bill.getInterest()), new BigDecimal(bill.getOverdueFee())
                    , new BigDecimal(bill.getOverdueServiceFee()));
            BigDecimal overdueFee = DecimalUtils.add(new BigDecimal(bill.getOverdueFee()), new BigDecimal(bill.getOverdueServiceFee()));
            RepaymentPlanDetailVO repaymentPlanDetailVO = new RepaymentPlanDetailVO()
                    .setOverdue(bill.getOverdueDays() > 0)
                    .setOverdueFee(bill.getOverdueDays() > 0 ? DecimalUtils.formatDecimal(overdueFee) : "")
                    .setPaid(Objects.equals(BillPaidStatusEnum.CLEAR_REPAYMENT, bill.getPaidStatus()))
                    .setRepaymentDate(DateUtil.getDate(bill.getRepaymentDate()))
                    .setBillNo(bill.getBillNo())
                    .setPrincipal(DecimalUtils.formatDecimal(new BigDecimal(bill.getBorrowCapital())))
                    .setRepaymentAmount("₹ " + DecimalUtils.formatDecimal(repaymentCapital))
                    .setCurrentPeriod(bill.getCurrentPeriod())
                    .setButtonText(Objects.equals(BillPaidStatusEnum.CLEAR_REPAYMENT, bill.getPaidStatus()) ? "Repaid" : "Repay Now")
                    .setInterest(DecimalUtils.formatDecimal(new BigDecimal(bill.getInterest())));

            repaymentPlanDetails.add(repaymentPlanDetailVO);
        }
        return repaymentPlanDetails;
    }

    private List<OrderFieldDetail> composeOrderFieldDetails(TradeInfoResponse tradeInfo, List<BillVO> bills, String contactUrl) {
        List<OrderFieldDetail> orderFieldDetails = Lists.newArrayList();
        BigDecimal needRepayAmount = BigDecimal.ZERO;
        BigDecimal interest = BigDecimal.ZERO;
        BigDecimal overdueFees = BigDecimal.ZERO;
        for (BillVO billVO : bills) {
            BigDecimal amount = CalculatorUtils.repaymentTotalCapital(new BigDecimal(billVO.getBorrowCapital()),
                    new BigDecimal(billVO.getInterest()), new BigDecimal(billVO.getOverdueFee())
                    , new BigDecimal(billVO.getOverdueServiceFee()));
            needRepayAmount = needRepayAmount.add(amount);
            interest = interest.add(new BigDecimal(billVO.getInterest()));
            overdueFees = DecimalUtils.add(new BigDecimal(billVO.getOverdueFee()), new BigDecimal(billVO.getOverdueServiceFee()));

        }
        if (CollectionUtils.isEmpty(bills)) {//未放款的状态
            Response<OfferResponse> offerListResponse = quoteQueryService.quoteDetail(tradeInfo.getProductCode());
            OfferResponse offerResponse = offerListResponse.getData();
            if (offerResponse != null) {
                needRepayAmount = DecimalUtils.add(new BigDecimal(offerResponse.getBorrowTotalCapital()), new BigDecimal(offerResponse.getTotalInterestFee()));
                interest = new BigDecimal(offerResponse.getTotalInterestFee());
            }
        }
        if (StringUtils.isBlank(contactUrl)) {
            contactUrl = apolloConfigUtil.getLoanAgreementsUrl();
        }
        OrderFieldDetail principle = new OrderFieldDetail("Amount", "1", new OrderFieldDetail.SchemeURL("₹ " + DecimalUtils.formatDecimal(new BigDecimal(tradeInfo.getBorrowTotalCapital())), ""));
        OrderFieldDetail duration = new OrderFieldDetail("Duration", "1", new OrderFieldDetail.SchemeURL(tradeInfo.getPeriodLength() + " Days", ""));
        OrderFieldDetail interestField = new OrderFieldDetail("Interest", "1", new OrderFieldDetail.SchemeURL("₹ " + DecimalUtils.formatDecimal(interest), ""));
        OrderFieldDetail serviceFee = new OrderFieldDetail("Service Fees", "1", new OrderFieldDetail.SchemeURL("₹ " + DecimalUtils.formatDecimal(new BigDecimal(tradeInfo.getTotalServiceFee())), ""));
        OrderFieldDetail receiveAmount = new OrderFieldDetail("Amount You Get", "1", new OrderFieldDetail.SchemeURL("₹ " + DecimalUtils.formatDecimal(new BigDecimal(tradeInfo.getReceivedTotalCapital())), ""));
        OrderFieldDetail repayAmount = new OrderFieldDetail("Amount You Repay", "1", new OrderFieldDetail.SchemeURL("₹ " + DecimalUtils.formatDecimal(needRepayAmount), ""));
        OrderFieldDetail agreement = new OrderFieldDetail("DHCP", "4", new OrderFieldDetail.SchemeURL("Loan Agreements", contactUrl));
        OrderFieldDetail orderId = new OrderFieldDetail("Order ID", "6", new OrderFieldDetail.SchemeURL(tradeInfo.getTradeNo(), ""));
        orderFieldDetails.add(principle);
        orderFieldDetails.add(duration);
        orderFieldDetails.add(interestField);
        orderFieldDetails.add(serviceFee);
        orderFieldDetails.add(receiveAmount);
        if (CollectionUtils.isNotEmpty(bills) && CalculatorUtils.isOverdue(bills.get(0).getRepaymentDate(), bills.get(0).getPayOffDate())) {
            OrderFieldDetail overdueDaysFiled = new OrderFieldDetail("Overdue Days", "5", new OrderFieldDetail.SchemeURL(CalculatorUtils.overDueDays(bills.get(0).getRepaymentDate(), bills.get(0).getPayOffDate()) + " Days", ""));
            OrderFieldDetail overdueFeesField = new OrderFieldDetail("Overdue Fees", "5", new OrderFieldDetail.SchemeURL("₹ " + DecimalUtils.formatDecimal(overdueFees), ""));
            orderFieldDetails.add(overdueDaysFiled);
            orderFieldDetails.add(overdueFeesField);
        }
        orderFieldDetails.add(repayAmount);
        orderFieldDetails.add(agreement);
        orderFieldDetails.add(orderId);
        return orderFieldDetails;
    }

    /**
     * 根据账单号获取应还金额
     *
     * @param pendingBillNos
     * @return
     */
    public RepayAmountInfo getRepayAmount(List<String> pendingBillNos) {
        RepayAmountInfo repayAmountInfo = new RepayAmountInfo();
        BigDecimal repayAmount = new BigDecimal(0);
        if (CollectionUtils.isEmpty(pendingBillNos)) {
            repayAmountInfo.setRepaymentAmount(repayAmount);
            return repayAmountInfo;
        }
        LOGGER.info("根据账单号获取应还金额,账单号:" + pendingBillNos.toString());
        for (String billNo : pendingBillNos) {
            Response<BillVO> queryByBillNo = billQueryService.queryByBillNo(billNo);
            if (queryByBillNo.getData() != null) {
                BillVO billVO = queryByBillNo.getData();
                repayAmount = this.repaymentAmount(billVO);
                repayAmountInfo.setBill(billVO);
            }
        }
        repayAmountInfo.setRepaymentAmount(repayAmount);
        LOGGER.info("根据账单号获取应还金额:" + repayAmount);
        return repayAmountInfo;
    }

    public BigDecimal getRepayAmountForBills(List<BillVO> bills) {
        BigDecimal repayAmount = new BigDecimal(0);
        if (CollectionUtils.isEmpty(bills)) {
            return repayAmount;
        }
        for (BillVO billVO : bills) {
            repayAmount = this.repaymentAmount(billVO);
        }
        LOGGER.info("根据账单获取应还金额:" + repayAmount);
        return repayAmount;
    }

    /**
     * 获取应还金额
     *
     * @param billVO
     * @return
     */
    public BigDecimal repaymentAmount(BillVO billVO) {
        BigDecimal repayAmount = CalculatorUtils.calculateRepaymentCapital(new BigDecimal(billVO.getRepaymentCapital()),
                new BigDecimal(billVO.getOverdueFee()), new BigDecimal(billVO.getOverdueServiceFee())
                , new BigDecimal(billVO.getPaidCapital()));
        return repayAmount;
    }

    /**
     * 还款
     */
    public RepayInfo repay(String tradeNo) {

        RepayInfo repayInfo = new RepayInfo();
        Object payInfo = this.payOrderBill(tradeNo);
        repayInfo.setPayInfo(payInfo);
        return repayInfo;
    }

    /**
     * 还款方式
     */
    public Object payOrderBill(String tradeNo) {

        LOGGER.info("== payOrderBill ==");
        TradeInfoResponse response = apiTradeService.getTradeInfoByTradeNo(tradeNo);
        if (response == null) {
            LOGGER.info("== payOrderBill trade not exist ==");
            throw new ApplicationException("trade not exist");
        }
        Response<UserInfo> userInfoByUserCode = userInfoService.getUserInfoByUserCode(response.getUserCode());
        ProductCategory productCategory = apiProductService.getProductCategoryByMerchantCode(response.getMerchantCode());
        BigDecimal repaymentCapital = new BigDecimal(response.getBorrowTotalCapital());

        if (!"loanZoneAll".equals(response.getProductCategory())) {
            try {
                BillVO billVO = billQueryService.queryBillByTradeNo(tradeNo).getData().get(0);
                Response<BillVO> billVOResponse = billQueryService.queryByBillNo(billVO.getBillNo());
                BillVO bill = billVOResponse.getData();
                repaymentCapital = CalculatorUtils.repaymentTotalCapital(new BigDecimal(bill.getBorrowCapital()),
                        new BigDecimal(bill.getInterest()), new BigDecimal(bill.getOverdueFee())
                        , new BigDecimal(bill.getOverdueServiceFee()));
            } catch (Exception e) {
                LOGGER.error("== payOrderBill  create error ==", e);
            }
        }

        Map<String, String> extrainfo = new HashMap<>();

        extrainfo.put("orderAmount", repaymentCapital.toString());
        extrainfo.put("tel", userInfoByUserCode.getData().getUser().getMobile());
        extrainfo.put("userName", userInfoByUserCode.getData().getUser().getName());

        RepayRequest repayRequest = new RepayRequest(tradeNo, "", JsonStream.serialize(extrainfo));
        String bizData = JSONObject.toJSONString(repayRequest);
//        boolean isWhiteUser = apolloConfigUtil.getWhiteUserCodes().contains(response.getUserCode());
        OpenApiResponse result = new OpenApiResponse()
                .setCode(0)
                .setMessage("h5 pay callback url mock");
        String repaymentUrl = "URL/repayResult";
//        if (isWhiteUser) {
        LOGGER.info("=== 还款请求链接res ===,start,productCategory:{},bizData:{}", JsonStream.serialize(productCategory), bizData);
        OpenApiResponse openApiResponse = CallHttpService.callMerchantMethod(productCategory.getMerchantDoaminUrl(), productCategory.getMerchantPublicKey(), "/cashbar/order/pay/url", productCategory.getMerchantCode(), bizData);
        LOGGER.info("=== 还款请求链接res11 ===,res:{}", JsonStream.serialize(openApiResponse));
        if (openApiResponse != null && openApiResponse.getCode() == BasicErrorCodeEnum.OPEN_API_SUCCESS.getCode() && openApiResponse.getData() != null) {
            String dataStr = JsonStream.serialize(openApiResponse.getData());
            repaymentUrl = apolloConfigUtil.getRepaymentLinkPrefix() + JsonIterator.deserialize(dataStr).get("jumpUrl").toString();
        }
//        }
        LOGGER.info("=== 还款请求链接res ===,res:{}", JsonStream.serialize(openApiResponse));

        result.setData(new RepayResultVO()
                .setPayType("1")
                .setRepayUrl(repaymentUrl));

        PaymentVO paymentVO = new PaymentVO();
        if (result != null && result.getCode() == BasicErrorCodeEnum.SUCCESS.getCode()) {
            RepayResultVO repayResultVO = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), RepayResultVO.class);
            if (repayResultVO.getPayType().equalsIgnoreCase("1")) {//H5还款
                paymentVO.setPlatform("H5");
                paymentVO.setPaymentLink(repayResultVO.getRepayUrl());
            } else {
                paymentVO.setDescription(repayResultVO.getDescription());
                paymentVO.setAmount(repayResultVO.getAmount());
                paymentVO.setCurrency(repayResultVO.getCurrency());
                paymentVO.setId(repayResultVO.getThirdOrderNo());
                paymentVO.setAppKey(repayResultVO.getThirdPayId());
                paymentVO.setName(repayResultVO.getName());
            }
            return paymentVO;
        }
        return paymentVO;
    }


    /**
     * 查询还款状态
     *
     * @param billNos
     * @return
     */
    public OrderStatusResponse queryRepaymentStatus(List<String> billNos) {
        Response<BillVO> billVOResponse = billQueryService.queryByBillNo(billNos.get(0));
        if (!billVOResponse.isSuccess() || billVOResponse.getData() == null) {
            throw new ApplicationException(OrderRestErrorEnum.ORDER_NOT_EXSIT);
        }

        BillPaidStatusEnum billPaidStatusEnum = Optional.ofNullable(billVOResponse).filter(Response::isSuccess).map(Response::getData)
                .map(BillVO::getPaidStatus).orElse(BillPaidStatusEnum.INIT);
        OrderStatusResponse orderStatusResponse = new OrderStatusResponse();
        if (billPaidStatusEnum == CLEAR_REPAYMENT) {
            orderStatusResponse.setRepaymentTips(RepaymentResultEnum.SUCCESS.getOfficialManage());
            orderStatusResponse.setRepaymentStatusImg(RepaymentResultEnum.SUCCESS.getPhotoUrl());
            orderStatusResponse.setRepaymentButtonText(RepaymentResultEnum.SUCCESS.getButtonMsg());
            orderStatusResponse.setRepaymentTitle(RepaymentResultEnum.SUCCESS.getTips());
        } else if (billPaidStatusEnum == BillPaidStatusEnum.INIT) {
            orderStatusResponse.setRepaymentTips(RepaymentResultEnum.PROCESSING.getOfficialManage());
            orderStatusResponse.setRepaymentStatusImg(RepaymentResultEnum.PROCESSING.getPhotoUrl());
            orderStatusResponse.setRepaymentButtonText(RepaymentResultEnum.PROCESSING.getButtonMsg());
            orderStatusResponse.setRepaymentTitle(RepaymentResultEnum.PROCESSING.getTips());
        } else {
            orderStatusResponse.setRepaymentTips(RepaymentResultEnum.FAILED.getOfficialManage());
            orderStatusResponse.setRepaymentStatusImg(RepaymentResultEnum.FAILED.getPhotoUrl());
            orderStatusResponse.setRepaymentButtonText(RepaymentResultEnum.FAILED.getButtonMsg());
            orderStatusResponse.setRepaymentTitle(RepaymentResultEnum.FAILED.getTips());
        }
        orderStatusResponse.setRepaymentStatus(RepaymentQueryStatusEnum.getByCode(billPaidStatusEnum.getStatus()).getMsg());
        return orderStatusResponse;
    }

    public void handlePayCallbackResult(String reqStr) {
        JSONObject jsonObject = JSONObject.parseObject(reqStr);
        String code = jsonObject.getString("code");
        String bizNo = jsonObject.getString("bizNo");
        String acceptAmount = jsonObject.getString("acceptAmount");
        String acceptNo = jsonObject.getString("acceptNo");
        String tripleAcceptDate = jsonObject.getString("tripleAcceptDate");
        String paymentChannelCode = jsonObject.getString("paymentChannelCode");
        String infoOrder = jsonObject.getString("infoOrder");
        String tripleNo = jsonObject.getString("tripleNo");
        String signature = jsonObject.getString("signature");
        String timestamp = jsonObject.getString("timestamp");
        //校验签名
        if (!verifySignature(timestamp, signature)) {
            LOGGER.error("还款回调 验签失败 reqStr:{}", reqStr);
            throw new ApplicationException("verifySignature failed");
        }
        LOGGER.info("还款回调信息，BizOrderNo: {}, tradeNo:{}, code:{}, infoOrder: {}.", bizNo, bizNo, code, infoOrder);
        if (StringUtils.isBlank(infoOrder)) {
            throw new ApplicationException(OrderRestErrorEnum.ORDER_BILL_ID_ERROR);
        }

        //会员费支付回调
        if (infoOrder.equalsIgnoreCase("UserServiceFee")) {
            ServiceFeePaymentCallBackRequest request = new ServiceFeePaymentCallBackRequest();
            request.setTripleSuccessDate(TimeUtils.getDateByStr(tripleAcceptDate, "yyyy-MM-dd HH:mm:ss"));
            request.setAcceptNo(acceptNo);
            request.setServiceNo(this.getServiceNo(bizNo));
            request.setPaidAmount(acceptAmount);
            tradeApplicationService.serviceFeePaymentCallBack(request);
            LOGGER.info("=== 用户会员费支付回调成功 request:{}===", JSONObject.toJSONString(request));
            return;
        }
        RepayOrderInfoDTO repayOrderInfo = JSONObject.parseObject(infoOrder, RepayOrderInfoDTO.class);

        if (!"0000".equals(code)) {
            LOGGER.error("支付失败: tradeNo: {}, BizOrderNo: {}, code: {}", bizNo, acceptNo, code);
            return;
        }

        BillPayRequest billPayRequest = new BillPayRequest();
        billPayRequest.setUserCode(repayOrderInfo.getUserCode());
        //金额拆分为 还款金额 和手续费
        BigDecimal channelServiceFee = Optional.ofNullable(repayOrderInfo.getChannelServiceFee()).orElse(BigDecimal.ZERO);
        billPayRequest.setPaidChannelServiceFee(channelServiceFee);
        billPayRequest.setCapital(new BigDecimal(acceptAmount).subtract(channelServiceFee).toString());
        billPayRequest.setBillNoList(repayOrderInfo.getBillNos());

        PaymentChannelEnum paymentChannel = PaymentChannelEnum.getEnumByCode(Integer.valueOf(paymentChannelCode));

        billPayRequest.setPaidChannel(paymentChannel);
        billPayRequest.setPayDate(TimeUtils.getDateByStr(tripleAcceptDate, "yyyy-MM-dd HH:mm:ss"));
        billPayRequest.setPayBizNo(acceptNo);
        billPayRequest.setBankNo(repayOrderInfo.getBankNo());
        billPayRequest.setTripleNo(tripleNo);
        billPayRequest.setDiscountList(repayOrderInfo.getDiscountList());
        billPayRequest.setOperator(repayOrderInfo.getOperator());
        LOGGER.info("== 还款推送到交易 ==,billPayRequest:{}", JSON.toJSONString(billPayRequest));
        Response<BillRepaymentResponse> response = billApplicationService.repaymentCallBackV2(billPayRequest);
        if (!response.isSuccess()) {
            throw new ApplicationException(response.getMsg());
        }

        //还款后评估额度
        try {
            apiUserQuotaService.evaluateQuota(repayOrderInfo.getUserCode(), repayOrderInfo.getProductCategory());
        } catch (Exception e) {
            LOGGER.warn("还款后评估额度失败,userCode:{}", repayOrderInfo.getProductCategory(), e);
        }
        LOGGER.info("................................账单已还, 交易号：{}, 账单号：{}, 交易金额：{}, 用户Code:{}", repayOrderInfo.getTradeNo(), repayOrderInfo.getBillNos(), repayOrderInfo.getRepaymentAmount(), repayOrderInfo.getUserCode());
    }

    private String getServiceNo(String bizNo) {
        if (bizNo.indexOf("_") != -1) {
            return bizNo.substring(0, bizNo.indexOf("_"));
        }
        return bizNo;
    }


    /**
     * 订单列表
     *
     * @param type
     * @param currentPage
     * @return
     */
    public RepaymentRecordsVO orderList(Integer type, Integer currentPage) {
        String userCode = RequestLocalInfo.getCurrentUser().getUserCode();
        RepaymentRecordsVO repaymentRecordsVO = new RepaymentRecordsVO();
        QueryUserTradeRequest queryUserTradeRequest = new QueryUserTradeRequest();
        queryUserTradeRequest.setUserCode(userCode);
        Page page = new Page();
        page.setCurrentPage(currentPage == null ? 1 : currentPage);
        page.setPageSize(10);
        queryUserTradeRequest.setPage(page);
        repaymentRecordsVO.setNoticeMessage(apolloConfigUtil.getOrderNotice());
        Response<UserTradeListResponse> userTradeListResponse = tradeQueryService.queryUserTradeList(queryUserTradeRequest);
        if (userTradeListResponse.getData() == null || CollectionUtils.isEmpty(userTradeListResponse.getData().getTradeList())) {
            return repaymentRecordsVO;
        }
        List<TradeVO> tradeList = userTradeListResponse.getData().getTradeList();
        List<RepaymentRecordVO> repaymentRecordVOS = new ArrayList<>();

        if (type == OrderRecordEnum.COMPLETED.getType()) {
            for (TradeVO tradeVO : tradeList) {
                boolean contains = apolloConfigUtil.getHideCategoriesInOrderList().contains(tradeVO.getProductCategory());
                boolean isLoanzone = "com.vayloan.app".equals(RequestLocalInfo.getRequestBasicInfo().getPackageName());
//                if (contains) {
//                    if (!isLoanzone) {
//                        continue;
//                    }
//                }
//
//                if (isLoanzone) {
//                    if (!contains) {
//                        continue;
//                    }
//                }

                boolean closed = TradeStatusEnum.NORMAL_REPAYMENT.equals(tradeVO.getTradeStatus()) ||
                        TradeStatusEnum.EARLY_REPAYMENT.equals(tradeVO.getTradeStatus()) ||
                        TradeStatusEnum.OVERDUE_REPAYMENT.equals(tradeVO.getTradeStatus()) ||
                        TradeStatusEnum.AUDIT_REJECT.equals(tradeVO.getTradeStatus()) ||
                        TradeStatusEnum.MANUAL_CLOSED.equals(tradeVO.getTradeStatus()) ||
                        TradeStatusEnum.PAY_RETRY.equals(tradeVO.getTradeStatus());
                if (closed) {
                    repaymentRecordVOS.add(this.buildRepaymentRecordVO(tradeVO));
                }
            }
        } else {
            for (TradeVO tradeVO : tradeList) {
                boolean contains = apolloConfigUtil.getHideCategoriesInOrderList().contains(tradeVO.getProductCategory());
                boolean isLoanzone = "com.vayloan.app".equals(RequestLocalInfo.getRequestBasicInfo().getPackageName());
//                if (contains) {
//                    if (!isLoanzone) {
//                        continue;
//                    }
//                }
//
//                if (isLoanzone) {
//                    if (!contains) {
//                        continue;
//                    }
//                }
                boolean notClosed = TradeStatusEnum.CREATE_SUCCESS.equals(tradeVO.getTradeStatus()) ||
                        TradeStatusEnum.AUDIT_WATTING.equals(tradeVO.getTradeStatus()) ||
                        TradeStatusEnum.LOAN_WAITTING.equals(tradeVO.getTradeStatus()) ||
                        TradeStatusEnum.REPAYMENT_WATTING.equals(tradeVO.getTradeStatus()) ||
                        TradeStatusEnum.OVERDUE.equals(tradeVO.getTradeStatus());
                if (notClosed) {
                    repaymentRecordVOS.add(this.buildRepaymentRecordVO(tradeVO));
                }
            }
        }
        repaymentRecordsVO.setHasNextPage(page.getCurrentPage() > userTradeListResponse.getData().getPage().getTotalPage());
        repaymentRecordsVO.setRepaymentRecords(repaymentRecordVOS);
        return repaymentRecordsVO;
    }

    /**
     * 构建订单记录
     *
     * @param tradeVO
     * @return
     */
    public RepaymentRecordVO buildRepaymentRecordVO(TradeVO tradeVO) {

        ProductCategory productCategory = apiProductService.getProductCategoryByMerchantCode(tradeVO.getMerchantCode());
        LOGGER.info("=== productCategory :{} ===", JSONObject.toJSONString(productCategory));
        OrderStatusDTO orderStatusDTO = new OrderStatusDTO()
                .setTradeCloseTypeEnum(tradeVO.getCloseTypeEnum())
                .setTradeAuditSubStatusEnum(tradeVO.getAuditSubStatus())
                .setTradeSignStatusEnum(tradeVO.getSignStatusEnum())
                .setTradeStatusEnum(tradeVO.getTradeStatus())
                .setTradeNo(tradeVO.getTradeNo())
                .setCategoryCode(tradeVO.getProductCategory());
        OrderStatusBriefInfo orderStatusBriefInfo = orderStatusHandler.getOrderStatusForApp(orderStatusDTO);
        RepaymentRecordVO repaymentRecordVO = new RepaymentRecordVO()
                .setStatusText(orderStatusBriefInfo.getBorrowStatusEnum().getTitle())
                .setJumpUrl(orderStatusBriefInfo.getJumpUrl())
                .setApplyDate(DateUtil.getDate(tradeVO.getApplyDate()))
                .setTradeNo(tradeVO.getTradeNo())
                .setProductCode(tradeVO.getProductCode())
                .setCategoryCode(tradeVO.getProductCategory())
                .setCategoryName(productCategory.getName())
                .setCategoryLogo(productCategory.getLogo())
                .setStatusText(orderStatusBriefInfo.getBorrowStatusEnum().getDesc())
                .setStatus(orderStatusBriefInfo.getBorrowStatusEnum().getCode())
                .setDuration(tradeVO.getPeriodLength() + "Days")
                .setAmount("₹" + DecimalUtils.formatDecimal(new BigDecimal(tradeVO.getBorrowCapital())));
        return repaymentRecordVO;
    }

    /**
     * 还款详情
     *
     * @param billNo
     * @return
     */
    public RepaymentDetail repaymentDetail(String billNo) {
        RepaymentDetail repaymentDetail = new RepaymentDetail();
        Response<BillVO> billVOResponse = billQueryService.queryByBillNo(billNo);
        BillVO bill = billVOResponse.getData();
        BigDecimal repaymentCapital = CalculatorUtils.repaymentTotalCapital(new BigDecimal(bill.getBorrowCapital()),
                new BigDecimal(bill.getInterest()), new BigDecimal(bill.getOverdueFee())
                , new BigDecimal(bill.getOverdueServiceFee()));
        repaymentDetail.setAmount("₹" + DecimalUtils.formatDecimal(repaymentCapital))
                .setCurrentPeriod(bill.getCurrentPeriod())
                .setInterest(DecimalUtils.formatDecimal(new BigDecimal(bill.getInterest())))
                .setPaid(Objects.equals(bill.getPaidStatus(), BillPaidStatusEnum.CLEAR_REPAYMENT))
                .setPrinciple(DecimalUtils.formatDecimal(new BigDecimal(bill.getBorrowCapital())))
                .setOverdueFees(DecimalUtils.formatDecimal(DecimalUtils.add(new BigDecimal(bill.getOverdueFee()), new BigDecimal(bill.getOverdueServiceFee()))))
                .setStatusText(Objects.equals(bill.getPaidStatus(), BillPaidStatusEnum.CLEAR_REPAYMENT) ? "Repaid" : "To Repay")
                .setRepayDate(DateUtil.getDate(bill.getRepaymentDate()));

        this.buildRepaymentDetail(repaymentDetail, bill);
        return repaymentDetail;
    }

    private void buildRepaymentDetail(RepaymentDetail repaymentDetail, BillVO bill) {
        LOGGER.info("== 还款详情 ==,repaymentDetail:{}", JSON.toJSONString(repaymentDetail));
        List<OrderFieldDetail> orderFieldDetails = Lists.newArrayList();
        OrderFieldDetail actualRepaymentDate = new OrderFieldDetail("Repayment Time", "1", new OrderFieldDetail.SchemeURL(DateUtil.getDate(bill.getPayOffDate()), ""));
        OrderFieldDetail actualRepaymentAmount = new OrderFieldDetail("Repayment Amount", "1", new OrderFieldDetail.SchemeURL("₹" + DecimalUtils.formatDecimal(new BigDecimal(bill.getPaidCapital())), ""));
        OrderFieldDetail paymentMethod = new OrderFieldDetail("Method of Payment", "1", new OrderFieldDetail.SchemeURL("H5", ""));
        orderFieldDetails.add(actualRepaymentDate);
        orderFieldDetails.add(actualRepaymentAmount);
        orderFieldDetails.add(paymentMethod);
        repaymentDetail.setOrderFieldDetails(orderFieldDetails);
    }

    private boolean verifySignature(String timestamp, String signature) {
        String calculatedSignature = MD5.md5(timestamp + SECRET);
        return Objects.equals(signature, calculatedSignature);
    }

    /**
     * 获取逾期天数
     *
     * @param userCode
     * @return
     */
    public Integer getMaxOverDueDays(String userCode) {
        Integer maxOverDueDays = 0;
        List<BillVO> bills = billQueryService.queryByUserCode(userCode).getData();
        if (CollectionUtils.isNotEmpty(bills)) {
            maxOverDueDays = bills.stream().map(BillVO::getOverdueDays).max(Integer::compareTo).get();
        }
        return maxOverDueDays;

    }

    /**
     * 待提现
     *
     * @param tradeNo
     */
    public void withdraw(String tradeNo) {
        Response<TradeInfoResponse> tradeInfoResponse = tradeQueryService.queryByTradeNo(tradeNo);
        if (tradeInfoResponse.getData() == null) {
            throw new ApplicationException(OrderRestErrorEnum.ORDER_NOT_EXSIT);
        }
        ProductCategory productCategory = apiProductService.getProductCategoryByMerchantCode(tradeInfoResponse.getData().getMerchantCode());
        Map param = Maps.newHashMap();
        param.put("order_no", tradeNo);
        param.put("needConfirm", productCategory.getConfirmWithdrawProcess() == 1 ? true : false);
        String bizData = JSONObject.toJSONString(param);
        OpenApiResponse result = CallHttpService.callMerchantMethod(productCategory.getMerchantDoaminUrl(), productCategory.getMerchantPublicKey(), "/gupta.payment.confirm", productCategory.getMerchantCode(), bizData);
        if (result != null && result.getCode() == BasicErrorCodeEnum.OPEN_API_SUCCESS.getCode()) {
            WithdrawCallbackRequest withdrawCallbackRequest = new WithdrawCallbackRequest();
            withdrawCallbackRequest.setTradeNo(tradeNo);
            Response response = tradeApplicationService.withdrawCallback(withdrawCallbackRequest);
            if (!response.isSuccess()) {
                throw new ApplicationException(BasicErrorCodeEnum.UNKNOW_ERROR);
            }
        }
    }

    /**
     * 支付服务费
     *
     * @param payAmount
     * @return
     */
    public ServiceFeeResponse servicePayment(String payAmount) {

        if (!payAmount.equalsIgnoreCase(apolloConfigUtil.getPayAmount())) {
            throw new ApplicationException("Please check the payment amount and refresh.");
        }
        String userCode = RequestLocalInfo.getCurrentUser().getUserCode();
        String channelCode = RequestLocalInfo.getRequestBasicInfo().getChannel();
        String appVersion = RequestLocalInfo.getRequestBasicInfo().getAppVersion();
        String appCode = RequestLocalInfo.getRequestBasicInfo().getAppCode();
        String packageName = RequestLocalInfo.getRequestBasicInfo().getPackageName();
        CreateServiceFeeRequest request = new CreateServiceFeeRequest();
        request.setUserCode(userCode);
        request.setPayAmount(new BigDecimal(payAmount));
        request.setChannelCode(channelCode);
        request.setAppVersion(appVersion);
        request.setAppCode(appCode);
        request.setPackageName(packageName);
        Response<CreateServiceResponse> serviceFeePayment = tradeApplicationService.createServiceFeePayment(request);
        if (!serviceFeePayment.isSuccess()) {
            throw new ApplicationException(serviceFeePayment.getMsg());
        }
        String serviceNo = serviceFeePayment.getData().getServiceNo();
        LOGGER.info("=== 创建服务费订单成功 serviceNo:{},userCode:{} ====", serviceNo, userCode);


        RepaymentRequest repaymentRequest = new RepaymentRequest();
        String bizNo = serviceNo + "_" + DateUtil.getDate(new Date(), "mmss");
        repaymentRequest.setProductCategoryCode("tezPaisaAll")
                .setUserCode(userCode)
                .setPaymentChannelEnum(PaymentChannelEnum.RAZORPAY);
        //通知给pay的金额为 还款金额+手续费
        BigDecimal repaymentAmount = new BigDecimal(payAmount);
        Response<UserInfo> rs = userInfoService.getUserInfoByUserCode(userCode);
        UserInfo userInfo = rs.getData();
        RepaymentTypeEnum repaymentTypeEnum = RepaymentTypeEnum.APP;
        if (apolloConfigUtil.useRazorpayType().equalsIgnoreCase("WAP")) {
            repaymentTypeEnum = RepaymentTypeEnum.WAP;
        }
        Response<UserBankInfo> userBankInfoResponse = userBankService.getByUserCode(userCode);
        UserBankInfo userBankInfo = userBankInfoResponse.getData();
        String repayNotifyUrl = apolloConfigUtil.getApiWebPath() + "/order/notify/pay";
        repaymentRequest.setBindMobile(userInfo.getUser().getMobile())
                .setRepayAmount(repaymentAmount.setScale(0, BigDecimal.ROUND_HALF_DOWN).toString())
                .setRepaymentTypeEnum(repaymentTypeEnum)
                .setRepaymentFundChannelEnum(RepaymentFundChannelEnum.NETBANKING)
                .setUrlReturn(apolloConfigUtil.getAppRepaymentReturnUrl())
                .setNotifyUrl(repayNotifyUrl)
                .setEmail(userInfo.getUserBase().getEmail())
                .setBankId(userBankInfo.getCode())
                .setAcctName(userInfo.getUser().getName())
                .setCardNo(userBankInfo.getCardNo())
                //pay有唯一性校验 用户多次发起付款需要加时间戳
                .setBizNo(bizNo)
                .setInfoOrder("UserServiceFee")
                .setAppVersion(appVersion);

        RepaymentPersonInfo repaymentPersonInfo = new RepaymentPersonInfo();
        repaymentPersonInfo.setAddress(userInfo.getUserResident().getAddress())
                .setCity(userInfo.getUserResident().getCity())
                .setState(userInfo.getUserResident().getProvince())
                .setCountry("India")
                .setFirstName(userInfo.getUserBase().getFirstName())
                .setLastName(userInfo.getUserBase().getLastName())
                .setPinCode(userInfo.getUserResident().getPinCode());
        repaymentRequest.setPersonInfo(repaymentPersonInfo);
        //防止重复支付
        String paymentKey = OrderConsts.CASHLOAN_REPAYMENT_KEY;
        redisClient.set(paymentKey, bizNo, apolloConfigUtil.getRepaymentRedisExpireSeconds(), serviceNo);
        redisClient.set(OrderConsts.CASHLOAN_REPAYMENT_CHANNEL_KEY, PaymentChannelEnum.RAZORPAY.getCode(), apolloConfigUtil.getRepaymentRedisExpireSeconds(), bizNo);
        Response<RepaymentResponse> repaymentResponse = payService.repayment(repaymentRequest);

        if (!repaymentResponse.isSuccess()) {
            throw new ApplicationException(repaymentResponse.getMsg());
        }
        if (repaymentResponse.getData() == null) {
            throw new ApplicationException(OrderRestErrorEnum.PAID_ERROR);
        }

        ServiceFeeResponse response = new ServiceFeeResponse();
        if (repaymentResponse.getData().getParams() instanceof String) {
            response.setPayInfo(repaymentResponse.getData().getParams());
        } else {
            response.setPayInfo(JSONObject.toJSONString(repaymentResponse.getData().getParams()));
        }
        response.setServiceNo(serviceNo);
        LOGGER.info("================================= repayment userCode:{}, payInfo:{} =================================", userCode, JSONObject.toJSONString(repaymentResponse.getData().getParams()));
        return response;
    }

    /**
     * 查询支付状态
     *
     * @param serviceNo
     * @return
     */
    public ServiceFeeQueryResponse serviceQuery(String serviceNo) {
        Response<ServiceTradeInfo> serviceTradeInfoResponse = tradeQueryService.queryServiceTradeInfo(serviceNo);
        if (serviceTradeInfoResponse.getData() == null) {
            throw new ApplicationException("payment order is not exist");
        }
        ServiceFeeQueryResponse response = new ServiceFeeQueryResponse();
        response.setServiceStatus(serviceTradeInfoResponse.getData().getServiceStatus());
        return response;
    }

    /**
     * 获取服务费
     *
     * @return
     */
    public ServiceFeeInfoResponse serviceInfo() {
        ServiceFeeInfoResponse response = new ServiceFeeInfoResponse();
        response.setPayAmount(apolloConfigUtil.getPayAmount());
        response.setTitle("Just 1 step left to get money");
        List<String> tips = Lists.newArrayList("No commitments, cancel anytime", "Loan 100% approved, refundable if not", "Unlimited number of loans");
        response.setTips(tips);
        return response;
    }
}
